/***************************************************
  dayend.c

  日结定时程序
  hatsusakana@gmail.com  
***************************************************/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <stdarg.h>

#ifdef _WIN32
#include <windows.h>
#else
#include <pthread.h>
#endif

#define CONFIG  "./dayend.ini"
#define LOG     "./log"

#ifdef _WIN32
#define snprintf _snprintf
#define vsnprintf _vsnprintf
#endif

/***************************************************
  封装
***************************************************/

/** pthread与window的锁接口 **/
#ifdef _WIN32
#define DE_MUTEX_LOCK CRITICAL_SECTION
#define DE_INIT(x) InitializeCriticalSection(x)
#define DE_FREE(x) DeleteCriticalSection(x)
#define DE_LOCK(x) EnterCriticalSection(x)
#define DE_UNLOCK(x) LeaveCriticalSection(x)
#else
#define DE_MUTEX_LOCK pthread_mutex_t
#define DE_INIT(x) pthread_mutex_init(x, NULL)
#define DE_FREE(x) pthread_mutex_destroy(x)
#define DE_LOCK(x) pthread_mutex_lock(x)
#define DE_UNLOCK(x) pthread_mutex_unlock(x)
#endif

typedef struct ThreadData {
    void *p;
    void (*thread_proc)(void *p);
} ThreadData;

/** pthread与window启动线程 **/
#ifdef _WIN32
static DWORD WINAPI de_thread (LPVOID param) {
    ThreadData *buf = (ThreadData *)param;
    buf->thread_proc(buf->p);
    free(param);
    return TRUE;
}
#else
static void *de_thread (void *param) {
    ThreadData *buf = (ThreadData *)param;
    buf->thread_proc(buf->p);
    free(param);
    return NULL;
}
#endif

/** 发起线程 **/
static int make_thread (void *p, void (*thread_proc)(void *p)) {
    ThreadData *p_data = NULL;

#ifndef _WIN32
    pthread_t thid;
    pthread_attr_t attr;
#endif

    if (!thread_proc)
        return 0;

    p_data = (ThreadData *)malloc(sizeof(ThreadData));
    if (!p_data)
        return 0;

    p_data->p = p;
    p_data->thread_proc = thread_proc;

#ifdef _WIN32
        CloseHandle(CreateThread(NULL, 0, de_thread, p_data, 0, 0));
#else
        pthread_attr_init(&attr);
        pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
        pthread_create(&thid, &attr, de_thread, p_data);
#endif
    return 1;
}

/** sleep(秒) **/
static void de_sleep (int sec) {
#ifdef _WIN32
    Sleep(sec * 1000);
#else
    sleep(sec);
#endif
}

/** 记录日志 **/
static void de_log (const char *fmt, ...) {
    va_list args;
    FILE *fp = NULL;
    struct tm *runtime;
    time_t nowtime;
    char buf[2048] = {0};

    time(&nowtime);
    runtime = localtime(&nowtime);

    //打开日志文件
    memset(buf, 0, sizeof(buf));
    snprintf(buf, sizeof(buf) - 1, "%s/%04d%02d%02d.txt", LOG, runtime->tm_year + 1900, runtime->tm_mon + 1, runtime->tm_mday);
    fp = fopen(buf, "ab+");
    if (!fp) fp = fopen(buf, "wb+");
    if (!fp)
        return;

    //写入时间
    memset(buf, 0, sizeof(buf));
    snprintf(buf, sizeof(buf) - 1, "%04d-%02d-%02d %02d:%02d:%02d", 
        runtime->tm_year + 1900, runtime->tm_mon + 1, runtime->tm_mday,
        runtime->tm_hour, runtime->tm_min, runtime->tm_sec);
    fwrite(buf, 1, strlen(buf), fp);
    fwrite("|", 1, 1, fp);

    //写入命令
    memset(buf, 0, sizeof(buf));
    va_start(args, fmt);
    vsnprintf(buf, sizeof(buf) - 1, fmt, args);
    va_end(args);
    fwrite(buf, 1, strlen(buf), fp);
    fwrite("\n", 1, 1, fp);
    fclose(fp);
}

/***************************************************
  主函数
***************************************************/

static void crontab (int year, int month, int day, int hour, int min, int sec, int wday);

/** 主函数 **/
int main (int argc, char *argv[]) {
    struct tm *runtime;
    time_t nowtime;

    while (1) {
        time(&nowtime);
        runtime = localtime(&nowtime);
        if (runtime->tm_sec != 0) {
            de_sleep(1);
            continue;
        }

        crontab(runtime->tm_year + 1900,
          runtime->tm_mon + 1,
          runtime->tm_mday,
          runtime->tm_hour,
          runtime->tm_min,
          runtime->tm_sec,
          runtime->tm_wday);
        
        de_sleep(1);
    }
    return 1;
}

/***************************************************
  定时器
***************************************************/

typedef struct Cnf {
    /**
     * 时间
     */
    int year;
    int month;
    int day;
    int hour;
    int min;
    int wday;

    /**
     * 执行类型
     * 
     * 1=>cmd执行
     */
    int type;

    /**
     * 扩展字符串
     */
     char msg[2048];
} Cnf;

static const char *check_number (const char *p) {
    if (!(*p >= '0' && *p <= '9'))
        return NULL;
    while (*p >= '0' && *p <= '9')
        p++;
    return p;
}

/** 解析cnf **/
static int cnf_read_decode (const char *p, Cnf *cnf) {
    const char *q = NULL;

    //year
    while (*p <= 32 && *p != 0) p++;
    if (*p == 0) return 0;
    if (*p == '*') {
        cnf->year = -1;
        p++;
    } else if ((q = check_number(p)) != NULL) {
        cnf->year = atoi(p);
        p = q;
    } else return 0;

    //month
    while (*p <= 32 && *p != 0) p++;
    if (*p == 0) return 0;
    if (*p == '*') {
        cnf->month = -1;
        p++;
    } else if ((q = check_number(p)) != NULL) {
        cnf->month = atoi(p);
        p = q;
    } else return 0;

    //day
    while (*p <= 32 && *p != 0) p++;
    if (*p == 0) return 0;
    if (*p == '*') {
        cnf->day = -1;
        p++;
    } else if ((q = check_number(p)) != NULL) {
        cnf->day = atoi(p);
        p = q;
    } else return 0;

    //hour
    while (*p <= 32 && *p != 0) p++;
    if (*p == 0) return 0;
    if (*p == '*') {
        cnf->hour = -1;
        p++;
    } else if ((q = check_number(p)) != NULL) {
        cnf->hour = atoi(p);
        p = q;
    } else return 0;

    //min
    while (*p <= 32 && *p != 0) p++;
    if (*p == 0) return 0;
    if (*p == '*') {
        cnf->min = -1;
        p++;
    } else if ((q = check_number(p)) != NULL) {
        cnf->min = atoi(p);
        p = q;
    } else return 0;

    //wday
    while (*p <= 32 && *p != 0) p++;
    if (*p == 0) return 0;
    if (*p == '*') {
        cnf->wday = -1;
        p++;
    } else if ((q = check_number(p)) != NULL) {
        cnf->wday = atoi(p);
        p = q;
    } else return 0;

    //|
    while (*p <= 32 && *p != 0) p++;
    if (*p == 0) return 0;
    if (*p++ != '|') return 0;

    //type
    while (*p <= 32 && *p != 0) p++;
    if (*p == 0) return 0;
    if ((q = check_number(p)) != NULL) {
        cnf->type = atoi(p);
        p = q;
    } else return 0;

    //|
    while (*p <= 32 && *p != 0) p++;
    if (*p == 0) return 0;
    if (*p++ != '|') return 0;
    while (*p <= 32 && *p != 0) p++;
    if (*p == 0) return 0;

    strncpy(cnf->msg, p, sizeof(cnf->msg) - 1);
    return 1;
}


/**
 * 配置定时器列表
 *
 * 0=>读取完毕
 * 1=>匹配成功
 * 2=>无效行
 */
static int cnf_read (FILE *fp, Cnf *cnf, int year, int month, int day, int hour, int min, int wday) {
    int ret = 0;
    char buf[4096] = {0}, *p = NULL;
    char buf2[4096] = {0};

    memset(cnf, 0, sizeof(Cnf));
    memset(buf, 0, sizeof(buf));
    memset(buf2, 0, sizeof(buf2));
    if (fgets(buf, sizeof(buf), fp)) {
        while (*buf != 0 && buf[strlen(buf) - 1] <= 32)
            buf[strlen(buf) - 1] = 0;

        if (*buf == 0 || *buf == '#')
            return 2;

        p = strstr(buf, "#");
        if (p) *p = 0;
        strcpy(buf2, buf);

        //解析
        if (!cnf_read_decode(buf, cnf)) {
            de_log("error %s", buf2);
            return 2;
        }

        //时间对比
        if (cnf->year != -1 && cnf->year != year) return 2;
        if (cnf->month != -1 && cnf->month != month) return 2;
        if (cnf->day != -1 && cnf->day != day) return 2;
        if (cnf->hour != -1 && cnf->hour != hour) return 2;
        if (cnf->min != -1 && cnf->min != min) return 2;
        if (cnf->wday != -1 && cnf->wday != wday) return 2;

        de_log("run %s", buf2);
        return 1;
    }
    return 0;
}

static int crontab_type1 (const char *cmd);

/** 定时器(分钟) **/
static void crontab (int year, int month, int day, int hour, int min, int sec, int wday) {
    int ret = 0;
    FILE *fp = NULL;
    Cnf cnf = {0};
    char *buf = NULL;

    de_log("-------------crontab running-------------");

    fp = fopen(CONFIG, "r");
    if (!fp) {
        de_log("config file error");
        return;
    }

    while ((ret = (cnf_read(fp, &cnf, year, month, day, hour, min, wday))) != 0) {
        if (ret == 1) {
            //cmd执行
            if (cnf.type == 1) {
                if (!crontab_type1(cnf.msg)) {
                    de_log("fork_app failed, type=%d, msg=%s",cnf.type, cnf.msg);
                }
            } else {
                de_log("error type %d", cnf.type);
            }
        }
    }

    fclose(fp);
}

/** crontab_type1 **/
static int crontab_type1 (const char *cmd) {
#ifdef _WIN32
    size_t len = strlen(cmd) + 100;
    char *msg_new = (char *)malloc(len);
    if (!msg_new)
        return 0;

    memset(msg_new, 0, sizeof(msg_new));
    sprintf(msg_new, "start /min %s 1>nul 2>nul", cmd);
    system(msg_new);
    free(msg_new);
    return 1;
#else
    size_t len = strlen(cmd) + 100;
    char *msg_new = (char *)malloc(len);
    if (!msg_new)
        return 0;

    memset(msg_new, 0, len);
    sprintf(msg_new, "%s > /dev/null 2>&1 &", cmd);
    system(msg_new);
    free(msg_new);
    return 1;
#endif
}


